<?php

	class context {
		private $__install_mode = false;
		
		//--------------------------------------------------------------
		protected static $__instance;
		
		protected $__service_pool;
		protected $__pool_stack;
		
		public function __construct(){
			$this->__pool_stack = array();
		}

		public static function instance()
		{
			if (null === self::$__instance) {
				self::$__instance = new self();
			}

			return self::$__instance;
		}

		//--------------------------------------------------------------
		
		public function register_service($service_name, $service){
			self::instance()->__service_pool[$service_name] = $service;
		}

		public function unregister_service($service_name){
			if (isset($this->__service_pool[$service_name])){
				unset($this->__service_pool[$service_name]);
			}
		}
		

		//save context state for debug
		public static function save(){
			$inst = self::instance();
			array_push($inst->__pool_stack, $inst->__service_pool);
		}

		public static function restore(){
			$inst = self::instance();
			if (!empty($inst->__pool_stack)){
				$inst->__service_pool = array_pop($inst->__pool_stack);
			}
		}
		
		private function is_registered($service_name){
			if (!isset($this->__service_pool[$service_name])){
				return false;
			}
			
			if (($service = $this->__service_pool[$service_name]) && $service->is_service()){
				return $service;
			}else{
				return false;
			}
		}
		
		private function load_service($service_name){
			$service = null;
			
			if (class_exists($service_name)) {
				$service = new $service_name;
				
				if ($this->__install_mode){
					$service->install();
				}
			}
			
			return $service;
		}
		
		public function get_service($service_name){
			$instance = self::instance();
			
			if($service = $instance->is_registered($service_name)){
				return $service;
			}else{
				
				
				$service = $instance->load_service($service_name);
				$instance->register_service($service_name,$service);
				
				return $service;
			}
		}

		//service wrapper
		//pre 5.3 workaround
		//context()->get("io")->msg("Message");
		public function get($service_name){
			return self::instance()->call($service_name);
		}
		
		public static function _callStatic($name, $args){
			return $this->call($name, $args);
		}
		
		public function call($name, $args = null){
			if ($service = self::instance()->get_service($name)){
				return $service;
			}
			
			//route вызывается ПОСЛЕ получения сервиса, чтобы циклический вызов service->__call->context->route->service
			//не загнал скрипт в сегфолт

			if ($route = self::instance()->route($name)){
				return call_user_func_array($route,$args);
			}else{
				context::get("io")->debug("failed to route method '{$name}'", IO_DEBUG_ERROR);
			}
		}

		private function route($method){
			#context::get("io")->msg("routing $method");

			$pool = self::instance()->__service_pool;
			
			//treat service pool as a stack.
			//last registered service have a priority in routing
			if (sizeof($pool)){
				foreach(array_reverse($pool, true) as $service_name=>$service_handle)
				{					
					/*NOTICE:
						Context is only able to route method call to textual method defenition
						It is not affectable by service __call magic method, because it fails on services like 'db'.
					*/
					if (method_exists($service_handle, $method)){		//tricky, do not trigger __call
						return array($service_handle, $method);
					}
				}
			}
			
			return null;
		}
		
		public function install(){
			$this->__install_mode = true;
		}
	}

?>
